﻿using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;
using TUnit.Assertions.Analyzers.Extensions;

namespace TUnit.Assertions.Analyzers;

/// <summary>
/// A sample analyzer that reports the company name being used in class declarations.
/// Traverses through the Syntax Tree and checks the name (identifier) of each class node.
/// </summary>
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class MixAndOrOperatorsAnalyzer : ConcurrentDiagnosticAnalyzer
{
    public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } =
        ImmutableArray.Create(Rules.MixAndOrConditionsAssertion);

    public override void InitializeInternal(AnalysisContext context)
    {
        context.RegisterOperationAction(AnalyzeOperation, OperationKind.Await);
    }

    private static void AnalyzeOperation(OperationAnalysisContext context)
    {
        if (context.Operation is not IAwaitOperation awaitOperation)
        {
            return;
        }

        // Check if the awaited type implements IAssertionSource<T> or inherits from Assertion<T>
        var awaitedType = awaitOperation.Operation.Type;
        var isAssertionSource = awaitedType?.AllInterfaces.Any(x =>
            x.GloballyQualifiedNonGeneric() is "global::TUnit.Assertions.Core.IAssertionSource") == true;
        var isAssertion = awaitedType?.BaseType != null &&
            IsAssertionType(awaitedType.BaseType);

        if (!isAssertionSource && !isAssertion)
        {
            return;
        }

        var chainedMethodCalls = awaitOperation?.Descendants().OfType<IPropertyReferenceOperation>().ToArray() ?? [];

        if (chainedMethodCalls.Any(x => x.Property.Name == "And")
            && chainedMethodCalls.Any(x => x.Property.Name == "Or"))
        {
            context.ReportDiagnostic(Diagnostic.Create(Rules.MixAndOrConditionsAssertion, awaitOperation?.Syntax.GetLocation()));
        }
    }

    private static bool IsAssertionType(INamedTypeSymbol? type)
    {
        if (type == null)
        {
            return false;
        }

        // Check if this type is Assertion<T>
        if (type.GloballyQualifiedNonGeneric() is "global::TUnit.Assertions.Core.Assertion")
        {
            return true;
        }

        // Check base type recursively
        return IsAssertionType(type.BaseType);
    }
}
